/**
 * Copyright (C) 2009-2012, KingHool.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses.
 */
package com.kinghool.smodel.io.bean;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;

import com.kinghool.ebos.commons.Platform;
import com.kinghool.ebos.commons.utils.Assert;
import com.kinghool.ebos.commons.utils.MethodUtils;
import com.kinghool.ebos.commons.utils.Utils;
import com.kinghool.smodel.IModel;
import com.kinghool.smodel.ITreeModel;
import com.kinghool.smodel.IXmlModel;

/**
 * 
 * @Author Allen Deng(dengtailin@gmail.com) 
 */
public class BeanChannelHelper {

	public static void syncObjectPropertyByChildAttribute(Object object, 
				                        ITreeModel model,
				                        String childQueryStr,
				                        String propertyName,
				                        boolean model2Obj) {
		syncObjectPropertyByChildAttribute(object, model, childQueryStr, propertyName, propertyName, model2Obj);
	}
	
	public static void syncObjectPropertyByChildAttribute(Object object, 
				                        ITreeModel model,
				                        String childQueryStr,
				                        String propertyName,
				                        String modelProperty,
				                        boolean model2Obj) {
		if (object != null && model != null) {
			ITreeModel child = model;
		    if (Assert.isNotNullNotEmpty(childQueryStr)) {
		    	child = model.queryFirstTreeChild(childQueryStr);
		    	if (child == null) {
					if (model2Obj) {
						//if it is model to object, the model is null, return
						return;
					}
					child = model.addChild(childQueryStr);
				}
		    }
			syncObjectPropertyByAttribute(object, child, propertyName, modelProperty, model2Obj);
		}
	}
	
	public static void syncObjectPropertyByAttribute(Object object, 
			                                        IModel model,
											        String propertyName,
							                        boolean model2Obj) {
		syncObjectPropertyByAttribute(object, model, propertyName, propertyName, model2Obj);
	}
	
	public static void syncObjectPropertyByAttribute(Object object, 
				                        IModel model,
				                        String propertyName,
				                        String modelProperty,
				                        boolean model2Obj) {
		if (object != null
			&& model != null
			&& Assert.isNotNullNotEmpty(propertyName)
			&& Assert.isNotNullNotEmpty(modelProperty)) {
			if (model2Obj) {
				Method setMethod = MethodUtils.getSetMethod(object.getClass(), propertyName);
				if (setMethod != null) {
					Class<?>[] paraTypes = setMethod.getParameterTypes();
					Object propertyValue = model.getTypedProperty(modelProperty, paraTypes[0]);
					try {
						MethodUtils.invokeMethod(object, setMethod, propertyValue);
					} catch (Exception e) {
						Platform.getLogManager().error("Fail to set field " + propertyName + " of Object " + object, e, object);
					} 
				}
			} else {
				Method getMethod = MethodUtils.getGetMethod(object.getClass(), propertyName);
				if (getMethod != null) {
					try {
						Object objValue = MethodUtils.invokeMethod(object, getMethod);
						model.setProperty(modelProperty, objValue);
					} catch (Exception e) {
						Platform.getLogManager().error("Fail to set property " + propertyName + " of IModel " + object, e, model);
					} 
				}
			}
		}
	}
	
	public static void syncObjectPropertyByChildText(Object object, 
								               IXmlModel model,
								               String childQueryStr,
								               String propertyName,
								               boolean model2Obj) {
		if (object != null && model != null) {
			ITreeModel child = model;
		    if (Assert.isNotNullNotEmpty(childQueryStr)) {
		    	child = model.queryFirstTreeChild(childQueryStr);
		    	if (child == null) {
					if (model2Obj) {
						//if it is model to object, the model is null, return
						return;
					} else if (getObjectProperty(object, propertyName) == null) {
						return;
					}
					child = model.addChild(childQueryStr);
				}
		    }
			syncObjectPropertyByText(object, (IXmlModel) child, propertyName, model2Obj);
		}
		
	}
	
	public static void syncObjectPropertyByText(Object object, 
				                               IXmlModel model,
				                               String propertyName,
				                               boolean model2Obj) {
		if (object != null && model != null && Assert.isNotNullNotEmpty(propertyName)) {
			if (model2Obj) {
				Method method = MethodUtils.getSetMethod(object.getClass(), propertyName);
				if (method != null) {
					String propertyStrValue = model.getText();
					try {
						Object propertyValue = Utils.getTypedProperty(method.getParameterTypes()[0], propertyStrValue);
						MethodUtils.invokeMethod(object, method, propertyValue);
					} catch (Exception e) {
						Platform.getLogManager().error("Fail to set field " + propertyName + " of Object " + object, e, object);
					}
				}
			} else {
				Method getMethod = MethodUtils.getGetMethod(object.getClass(), propertyName);
				if (getMethod != null) {
					try {
						Object objValue = MethodUtils.invokeMethod(object, getMethod);
						model.setText(objValue == null ? "" : String.valueOf(objValue));
					} catch (Exception e) {
						Platform.getLogManager().error("Fail to set property " + propertyName + " of IModel " + object, e, model);
					} 
				}
			}
		}
	}
	
	public static Object[] getObjectPropertyArray(Object object, Method getArrayMethod) {
		if (getArrayMethod != null) {
			try { 
				if (Assert.isArray(getArrayMethod.getReturnType())) {
					Object result = MethodUtils.invokeMethod(object, getArrayMethod);
					return Utils.toArray(result);
				} 
			} catch (Exception e) {
				Platform.getLogManager().error("Fail to invoke method " + getArrayMethod.getName() + " of Object " + object, e, object);
			}
		}
		return new Object[0];
	}
	
	@SuppressWarnings("unchecked")
	public static void clearObjectPropertyArray(Object object, Field field) {
		if (field != null) {
			if (!Assert.isArray(field.getType())) {
				throw new IllegalArgumentException("field should be array object.");
			}
			if (Collection.class.isAssignableFrom(field.getType())) {
				boolean accessible = field.isAccessible();
				if (!accessible) {
					field.setAccessible(true);
				}
				try {
					field.set(object, Utils.newCollection((Class<? extends Collection<?>>) field.getType()));
					field.setAccessible(accessible);
				} catch (Exception e) {
					//ignore, the access exception will not exists
				} 
			} else if (Object[].class.isAssignableFrom(field.getType())) {
				boolean accessible = field.isAccessible();
				if (!accessible) {
					field.setAccessible(true);
				}
				try {
					field.set(object, Array.newInstance(field.getType(), 0));
					field.setAccessible(accessible);
				} catch (Exception e) {
					//ignore, the access exception will not exists
				}
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public static <T> void setObjectPropertyArray(T object, Field field, List<T> values) {
		if (field != null) {
			if (!Assert.isArray(field.getType())) {
				throw new IllegalArgumentException("field should be array object, such as List, Set, Object[].");
			}
			if (Collection.class.isAssignableFrom(field.getType())) {
				boolean accessible = field.isAccessible();
				if (!accessible) {
					field.setAccessible(true);
				}
				Collection<T> _values = Utils.newCollection((Class<? extends Collection<T>>) field.getType());
				_values.addAll(values);				
				try {
					field.set(object, _values);
					field.setAccessible(accessible);
				} catch (Exception e) {
					//ignore, the access exception will not exists
				}
				
			} else if (Object[].class.isAssignableFrom(field.getType())) {
				boolean accessible = field.isAccessible();
				if (!accessible) {
					field.setAccessible(true);
				}
				
				Object array = Array.newInstance(values.get(0).getClass(), values.size());    
		        for (int i = 0; i < values.size(); i++) {
		        	Array.set(array, i, values.get(i));  
		        }		        
				try {
					field.set(object, array);
					
					field.setAccessible(accessible);
				} catch (Exception e) {
					//ignore, the access exception will not exists
				}
			}
		}
	}
	
	public static Object getObjectProperty(Object object, String propertyName) {
		Method getMethod = MethodUtils.getGetMethod(object.getClass(), propertyName);
		if (getMethod != null) {
			try { 
				return MethodUtils.invokeMethod(object, getMethod);
			} catch (Exception e) {
				Platform.getLogManager().error("Fail to get field " + propertyName + " of Object " + object, e, object);
			}
		}
		return null;
	}
	
	public static void syncBean2Object(Object object, String propertyName, Object bean) {
		Method method = MethodUtils.getSetMethod(object.getClass(), propertyName);
		if (method != null) {
			try { 
				MethodUtils.invokeMethod(object, method, bean);
			} catch (Exception e) {
				Platform.getLogManager().error("Fail to set field " + propertyName + " of Object " + object, e, object);
			}
		}
	}
}
